package com.sc;

import org.junit.Test;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;

/**
 * Created by Shen Chao on 2018-06-12-012.
 * 线程并发编程
 * CountDownLatch用法
 * CyclicBarrier用法
 * Semaphore用法
 */
public class BingFaBianCheng {


    /**
     * CountDownLatch类位于java.util.concurrent包下，利用它可以实现类似计数器的功能。
     * 比如有一个任务A，它要等待其他4个任务执行完毕之后才能执行，此时就可以利用CountDownLatch来实现这种功能了。
     */
    @Test
    public void countDownLatch() {
        //参数为计数值
        CountDownLatch countDownLatch = new CountDownLatch(2);

        new Thread(() -> {
            try {
                System.out.println("子线程：" + Thread.currentThread().getName() + "正在执行");
                Thread.sleep(2000);
                System.out.println("子线程：" + Thread.currentThread().getName() + "执行完毕");
                //将count值减1
                countDownLatch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }) {
        }.start();

        new Thread(() -> {
            try {
                System.out.println("子线程: " + Thread.currentThread().getName() + "正在执行");
                Thread.sleep(2000);
                System.out.println("子线程: " + Thread.currentThread().getName() + "执行完毕");
                //将count值减1
                countDownLatch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        try {
            System.out.println("等待两个线程执行完毕");
            //await()会使线程挂起，直到count为0才继续执行
            //            countDownLatch.await();
            //等待一定时间，count还没为0，继续执行
            countDownLatch.await(1000, TimeUnit.MILLISECONDS);
            System.out.println("两个线程执行完毕");
            System.out.println("继续执行主线程");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    /**
     * 字面意思：回环栅栏
     * 通过它可以实现让一组线程等待至某个状态之后再全部同时执行。
     * 叫做回环是因为当所有等待线程都被释放以后，CyclicBarrier可以被重用。
     * 我们暂且把这个状态就叫做barrier，当调用await()方法之后，线程就处于barrier了。
     */
    /**
     * 假若有若干个线程都要进行写数据操作，并且只有所有线程都完成写数据操作之后，
     * 这些线程才能继续做后面的事情，此时就可以利用CyclicBarrier了：
     */
    @Test
    public void cyclicBarier() {
        int N = 4;
        CyclicBarrier cyclicBarrier = new CyclicBarrier(N);
        for (int i = 0; i < N; i++) {
            new Writer(cyclicBarrier).start();
        }
    }

    static class Writer extends Thread {
        private CyclicBarrier cyclicBarrier;

        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            try {
                System.out.println("线程：" + Thread.currentThread().getName() + "正在写数据");
                //模拟写数据
                Thread.sleep(5000);
                System.out.println("线程：" + Thread.currentThread().getName() + "写数据完毕，等待其他线程");
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println("所有线程执行完毕，继续执行");
        }
    }
}
